//
// Created by Administrator on 2021/10/21.
//

#include "BufferGeometry.h"

// 绘制模式 Draw Mode
const static std::map<int, const char*> dm_items = {
        std::make_pair(GL_POINTS, "GL_POINTS"),
        std::make_pair(GL_LINES, "GL_LINES"),
        std::make_pair(GL_LINE_LOOP, "GL_LINE_LOOP"),
        std::make_pair(GL_LINE_STRIP, "GL_LINE_STRIP"),
        std::make_pair(GL_TRIANGLES, "GL_TRIANGLES"),
        std::make_pair(GL_TRIANGLE_STRIP, "GL_TRIANGLE_STRIP"),
        std::make_pair(GL_TRIANGLE_FAN, "GL_TRIANGLE_FAN"),
};

// 多边形显示模式 Polygon Mode
const std::map<int, const char*> pm_items = {
        std::make_pair(GL_FILL, "GL_FILL"),
        std::make_pair(GL_LINE, "GL_LINE"),
        std::make_pair(GL_POINT, "GL_POINT"),
        std::make_pair(GL_RED, "GL_RED"),
        std::make_pair(GL_GREEN, "GL_GREEN"),
        std::make_pair(GL_BLUE, "GL_BLUE"),
        std::make_pair(GL_BLUE, "GL_ALPHA"),
        std::make_pair(GL_BLUE, "GL_RGB"),
        std::make_pair(GL_BLUE, "GL_RGBA"),
};

static int ct = 0;

BufferGeometry::BufferGeometry() {
    id = ++ct;
    uuid = String::toString("%p", this);
    type = GET_CLASS(__FILE__);
    name = type + "_" + id;

    this->bDrawIndices = false;
    this->drawCount = 0;

    // 默认 fill
    this->polygonMode = GL_FILL;
    // 默认三角面
    this->drawMode = GL_TRIANGLES;
    this->p_material = NULL;
}

BufferGeometry::~BufferGeometry() {
    log_info("gc BufferGeometry.%p", this);
    std::vector<float>().swap(vertices);
    std::vector<float>().swap(colors);
    std::vector<float>().swap(uvs);
    std::vector<float>().swap(normals);

    std::vector<int>().swap(indices);
    //SAFE_DELETE(p_material); Object3D提供的指针，由Object3D释放
}

/**
 * 应用材质
 * @param p_material 材质
 */
void BufferGeometry::applyMaterial(Material *p_material) {
    SAFE_DELETE(this->p_material);
    this->p_material = p_material;
}

/**
 * 初始化
 */
void BufferGeometry::onInit() {
    log_info("BufferGeometry onInit. %p", this);

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    if(!vertices.empty()){
        setUpAttribute(ShaderAttribute(Config::DEFAULT_ATTRIB_NAME_VERTEX, GL_FLOAT, 3), vertices);

        if(!colors.empty()){
            setUpAttribute(ShaderAttribute(Config::DEFAULT_ATTRIB_NAME_COLOR,  GL_FLOAT,4), colors);
        }

        if(!uvs.empty()){
            setUpAttribute(ShaderAttribute(Config::DEFAULT_ATTRIB_NAME_UV,  GL_FLOAT,2), uvs);
        }

        if(!normals.empty()){
            setUpAttribute(ShaderAttribute(Config::DEFAULT_ATTRIB_NAME_NORMAL,  GL_FLOAT,3), normals);
        }

        if(!indices.empty()){
            setUpIndices();
        }
    }

    glBindVertexArray(0);
}


/**
 * 给attribute赋值
 * @param attrib attri属性
 * @param data 数组值
 */
template<typename T>
void BufferGeometry::setUpAttribute(const ShaderAttribute &attrib, std::vector<T> data) {
    MAssert::check(p_material);
    Shader *p_shader = p_material->p_shader;
    if(p_shader->check()){
        GLint location = glGetAttribLocation( p_shader->getProgram(), attrib.attribName.c_str());
        if(location >= 0){
            glBindVertexArray(vao);

            if(Config::DEFAULT_ATTRIB_NAME_VERTEX == attrib.attribName){
                drawCount = data.size() / attrib.numComponent;
            }

            if(Config::DEFAULT_ATTRIB_NAME_COLOR == attrib.attribName){
                p_material->useVertexColor = true;
            }

            GLuint vbo;
            glGenBuffers(1, &vbo);
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBufferData(GL_ARRAY_BUFFER, sizeof(data[0]) * data.size(), &data[0], GL_STATIC_DRAW);
            glEnableVertexAttribArray(location);
            glVertexAttribPointer(location, attrib.numComponent, attrib.dataType, attrib.normalize, 0, (void *) 0);

            glBindVertexArray(0);
        }else {
            log_warn("未从shader:%s中获取到%s的变量地址", p_shader->getName().c_str(), attrib.attribName.c_str());
        }
    }else {
        log_warn("shader指针对象不存在%p", p_shader);
    }
}

/**
 * 索引配置
 * @param indices
 */
void BufferGeometry::setUpIndices() {
    glBindVertexArray(vao);
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);
    bDrawIndices = true;
    drawCount = indices.size();
    glBindVertexArray(0);
}

/**
 * 渲染
 * @param elapsedTime 时间
 */
void BufferGeometry::onRender(float elapsedTime, Matrix4x4 proj, Matrix4x4 view, Matrix4x4 pworld) {
    if(MAssert::isNull(p_material)) return;
    Shader *shader = p_material->p_shader;
    if(MAssert::isNull(shader)) return;
    if(MAssert::isNull(shader) || !shader->check() || drawCount <= 0) return;

    // use program
    glUseProgram(shader->getProgram());

    // set uniform
    shader->setUniform("u_world", pworld);
    shader->setUniform("u_view", view);
    shader->setUniform("u_proj", proj);
    shader->setUniform("use_vertex_color", p_material->useVertexColor);
    shader->setUniform("base_color", p_material->baseColor);
    shader->setUniform("u_pointSize", p_material->pointSize);

    // setting GL_FRONT_AND_BACK
    glPolygonMode(GL_FRONT_AND_BACK, polygonMode);
    if(GL_POINTS == drawMode){
        //点阵模式绘制
        glPointSize(p_material->pointSize);
    }

    // draw
    glBindVertexArray(vao);
    if(bDrawIndices) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glDrawElements(drawMode, drawCount, GL_UNSIGNED_INT, 0);
    } else {
        glDrawArrays(drawMode, 0 , drawCount);
    }
    glBindVertexArray(0);
}

/**
 * 绘制GUI
 */
void BufferGeometry::getUI() {
    {
        const char* dm_combo_preview_value;
        auto it = dm_items.find(drawMode);
        if(it != dm_items.end()){
            dm_combo_preview_value = it->second;
        }

        if (ImGui::BeginCombo(String::toString("Draw Model_%s", name.c_str()).c_str(), dm_combo_preview_value))
        {
            for (auto iterator = dm_items.begin(); iterator != dm_items.end(); ++iterator) {
                const bool is_selected = (drawMode == iterator->first);
                if (ImGui::Selectable(iterator->second, is_selected))
                    drawMode = iterator->first;
                if (is_selected)
                    ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
    }
    {
        const char* pm_combo_preview_value;
        auto it = pm_items.find(polygonMode);
        if(it != pm_items.end()){
            pm_combo_preview_value = it->second;
        }

        if (ImGui::BeginCombo(String::toString("Polygon Model_%s", name.c_str()).c_str(), pm_combo_preview_value))
        {
            for (auto iterator = pm_items.begin(); iterator != pm_items.end(); ++iterator) {
                const bool is_selected = (polygonMode == iterator->first);
                if (ImGui::Selectable(iterator->second, is_selected))
                    polygonMode = iterator->first;
                if (is_selected)
                    ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
    }
    ImGui::Text("Vertex Size: %d", vertices.size());
    ImGui::Text("bDraw Indices: %s", bDrawIndices ? "true" : "false");
    ImGui::Text("Draw Count: %d", drawCount);
    ImGui::Text("Indices: ");
    ImGui::SameLine();
    vectorToImGuiText(indices, 12);
}

















